////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Copyright (C), Crytek Studios, 2010.
// -------------------------------------------------------------------------
//
//  Description: ScriptBind function from the IK Interaction Manager
//
// -------------------------------------------------------------------------
//  History:
//  Created:     Nov 12th, 2010 by Michelle Martin
//
////////////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include "ScriptBind_IKInteractions.h"
#include "IKInteractionManager.h"		// for starting interactions


//------------------------------------------------------------------------

void CScriptBind_IKInteractions::RegisterMethods()
{
#undef SCRIPT_REG_CLASSNAME
#define SCRIPT_REG_CLASSNAME &CScriptBind_IKInteractions::

	SCRIPT_REG_FUNC(StartInteraction);
	SCRIPT_REG_TEMPLFUNC(StopInteraction, "interactionId");
	SCRIPT_REG_TEMPLFUNC(SetInteractionOffset, "interactionId, limbName, offsetPos, offsetRot");
}

//------------------------------------------------------------------------
CScriptBind_IKInteractions::CScriptBind_IKInteractions(ISystem *pSystem, CIKInteractionManager* pIKM)
{
//	m_pSystem = pSystem;
	m_pSS = pSystem->GetIScriptSystem();
//	m_pEntitySystem = gEnv->pEntitySystem;
	m_pIKManager = pIKM;
	CRY_ASSERT_MESSAGE(m_pIKManager, "CIKInteractionManager NULL Pointer received. Will crash.");

	Init( m_pSS );
	SetGlobalName("IKInteractionSystem");

	RegisterGlobals();
	RegisterMethods();
}

//------------------------------------------------------------------------

CScriptBind_IKInteractions::~CScriptBind_IKInteractions()
{
}

//------------------------------------------------------------------------

void CScriptBind_IKInteractions::RegisterGlobals()
{
	SCRIPT_REG_GLOBAL(EIKObject_Default);
}

//------------------------------------------------------------------------

int CScriptBind_IKInteractions::StopInteraction( IFunctionHandler* pH, int interactionId )
{

	CIKInteractionManager* pInteractionManager = CCryAction::GetCryAction()->GetIKInteractionManager();
	if (!pInteractionManager)
		return pH->EndFunction(-1);

	bool retVal = pInteractionManager->StopIKInteraction(interactionId);
	return pH->EndFunction((int)retVal);
}

//------------------------------------------------------------------------

int CScriptBind_IKInteractions::StartInteraction(IFunctionHandler* pH)
{
	// This function can take an arbitrary amount of parameters:
	// Is the minimum amount of parameters present?
	int paramCount = pH->GetParamCount();
	int limbParameterStartIndex = 5; 
	
	if (paramCount < limbParameterStartIndex)
	{
		CryWarning(VALIDATOR_MODULE_GAME, VALIDATOR_WARNING, "StartInteraction called with too few parameters. Minimum required: EntityID, EntityID, IKHandle01");
		return pH->EndFunction(-1);
	}

	// Get the parameters
	ScriptHandle entityID, targetEntityID;
	int	objectType = 0;
	bool allowRegrip = true;
	pH->GetParam(1, entityID);
	pH->GetParam(2, targetEntityID);
	pH->GetParam(3, objectType);
	pH->GetParam(4, allowRegrip);

	// read in all the IK Handles that are passed in
	std::vector<uint64> ikLimbs;
	for (int i = limbParameterStartIndex; i <= paramCount; ++i)
	{
		const char* limbName;
		pH->GetParam(i, limbName);
		if (strcmp(limbName, "") != 0)
			ikLimbs.push_back(*((uint64*)limbName));
	}


	SIKInteractionStartParameters interactionParams((EntityId)entityID.n, (EntityId)targetEntityID.n, 0, (EIK_Object_Types)objectType, ikLimbs);
	if (!allowRegrip)	interactionParams.m_flags |= EIAC_NOREGRIP;


	// Retrieve optional Parameters from entity (if they exist)
	IEntity* targetEntity = gEnv->pEntitySystem->GetEntity((EntityId)targetEntityID.n);
	if (!targetEntity)
	{
		CryWarning(VALIDATOR_MODULE_GAME, VALIDATOR_ERROR, "IK Interaction: Target Entity with id %i doesn't exist. Failing Interaction.", (EntityId)targetEntityID.n);
		return pH->EndFunction(-1);
	}

	ReadAditionalPropertiesFromEntity(targetEntity, &interactionParams);

	// starting the new interaction
	CIKInteractionManager* pInteractionManager = CCryAction::GetCryAction()->GetIKInteractionManager();
	if (!pInteractionManager)
	{
		//CryWarning(VALIDATOR_MODULE_GAME, VALIDATOR_ERROR, "IK Interaction Manager doesn't exist. Failing Interaction.", targetEntityID);
		return pH->EndFunction(-1);
	}

	int retVal = pInteractionManager->StartNewIKInteraction(interactionParams);

	return pH->EndFunction(retVal);
}


//------------------------------------------------------------------------


void CScriptBind_IKInteractions::ReadAditionalPropertiesFromEntity( IEntity* targetEntity, SIKInteractionStartParameters *interactionParams )
{
	SmartScriptTable targetEntityTable = targetEntity->GetScriptTable();
	if (!targetEntityTable)
		return;

	SmartScriptTable propertiesTable;
	if (targetEntityTable->GetValue("Properties", propertiesTable))
	{
		SmartScriptTable interactionParamsTable;
		if (propertiesTable->GetValue("InteractionParameters", interactionParamsTable))
		{
			// EXTRACT all additional optional info here

			//  additional offset for this object
			if (interactionParamsTable->HaveValue("AdditionalOffsetPos"))
				interactionParamsTable->GetValue("AdditionalOffsetPos", interactionParams->m_additionalOffsetPos);
			if (interactionParamsTable->HaveValue("AdditionalOffsetRot"))
			{
				Vec3 additionalRotation;
				interactionParamsTable->GetValue("AdditionalOffsetRot", additionalRotation);
				// degree to radian conversion
				additionalRotation *= ((float)(g_PI)/180.0f);
				interactionParams->m_additionalOffsetRot.SetRotationXYZ(Ang3(additionalRotation));
			}

			// character animations setup for this interaction
			SmartScriptTable animationsTable;
			if (interactionParamsTable->GetValue("Animations", animationsTable))
			{

				// AnimationGraph Info
				if (animationsTable->HaveValue("sGraphInput"))
				{
					char *temp;
					animationsTable->GetValue("sGraphInput", temp);
					interactionParams->m_graphInputName = temp;
					if (animationsTable->HaveValue("sGraphValue"))
					{
						animationsTable->GetValue("sGraphValue", temp);
						interactionParams->m_graphInputValue = temp;
					}
				}

				// Animation Asset Name
				if (animationsTable->HaveValue("sAnimName"))
				{
					char *temp;
					animationsTable->GetValue("sAnimName", temp);
					interactionParams->m_animName = temp;
					if (animationsTable->HaveValue("fAnimBlendTime"))
						animationsTable->GetValue("fAnimBlendTime", interactionParams->m_animBlendTime);
					if (animationsTable->HaveValue("bAnimPausesGraph"))
						animationsTable->GetValue("bAnimPausesGraph", interactionParams->m_animPausesGraph);
					if (animationsTable->HaveValue("iAnimLayer"))
						animationsTable->GetValue("iAnimLayer", interactionParams->m_animLayer);
				}

				// Limb End Effector Assets
				int limbCount = interactionParams->m_ikHandles.size();
				for (int i = 0; i < limbCount; ++i)
				{
					string keyName;
					keyName.Format("sLimb%iAnimation", i + 1);
					if (animationsTable->HaveValue(keyName.c_str()))
					{
						char *temp;
						animationsTable->GetValue(keyName.c_str(), temp);
						if (temp[0] != '\0')
							interactionParams->m_limbAnimations[interactionParams->m_ikHandles.at(i)] = temp;
					}
				}
			}

			// add additional properties to extract below here


			// END OF EXTRACT all additional info here
		}
	}
}

//------------------------------------------------------------------------

int CScriptBind_IKInteractions::SetInteractionOffset( IFunctionHandler* pH, int interactionId, Vec3 pos, Vec3 rot )
{
	CIKInteractionManager* pInteractionManager = CCryAction::GetCryAction()->GetIKInteractionManager();
	if (!pInteractionManager)
	{
		CryWarning(VALIDATOR_MODULE_GAME, VALIDATOR_ERROR, "IK Interaction Manager doesn't exist. Failing call to SetInteractionOffset.");
		return pH->EndFunction(-1);
	}

	// degree to radian
	rot *= ((float)(g_PI)/180.0f);

	// create a quatT to store the translation and rotation
	QuatT newOffset;
	newOffset.t = pos;
	newOffset.q.SetRotationXYZ(Ang3(rot));
	
	// tell the interaction manager to update the offset
	pInteractionManager->SetOffsetForInteraction(interactionId, newOffset);

	return pH->EndFunction(1);
}

//------------------------------------------------------------------------

